﻿#include "pch.h"
#if _QG_USE_GL
#include "qgdrv_gl_rdh.h"
#include "qgdrv_gl_cnv.h"
#include "qgdrv_gl_cq.h"

//
void GLRdh::_CommitDhslState() throw()
{
	const GLDhslProperty& n = _pend.dhsl_prop;
	GLDhslProperty& p = _sion.dhsl_prop;

	// 깊이 사용
	if (p.zenable != n.zenable)
	{
		if (!n.zenable)
			glDisable(GL_DEPTH_TEST);
		else
		{
			glEnable(GL_DEPTH_TEST);

			if (p.zfunc != n.zfunc)
			{
				glDepthFunc(n.zfunc);

				p.zfunc = n.zfunc;
			}
		}

		p.zenable = n.zenable;
	}

	// 깊이 쓰기
	if (p.zwrite != n.zwrite)
	{
		glDepthMask(n.zwrite);

		p.zwrite = n.zwrite;
	}

	// 스텐실 켜기
	if (p.stencil != n.stencil)
	{
		if (p.twoside != n.twoside)
		{
			p.twoside = n.twoside;
			p.front_func = QGGL_INVALID_ENUM;
			p.front_fail = QGGL_INVALID_ENUM;
			p.front_pass = QGGL_INVALID_ENUM;
			p.front_zfail = QGGL_INVALID_ENUM;
			p.back_func = QGGL_INVALID_ENUM;
			p.back_fail = QGGL_INVALID_ENUM;
			p.back_pass = QGGL_INVALID_ENUM;
			p.back_zfail = QGGL_INVALID_ENUM;
			p.mask_read = QGGL_INVALID_UINT;
		}

		if (!n.stencil)
			glDisable(GL_STENCIL_TEST);
		else
		{
			// 설정
			if (n.twoside)
			{
				// 앞면
				if (p.front_func != n.front_func ||
					p.mask_read != n.mask_read)
				{
					glStencilFuncSeparate(GL_BACK, n.front_func, 0, n.mask_read);

					p.front_func = n.front_func;
				}

				if (p.front_pass != n.front_pass ||
					p.front_fail != n.front_fail ||
					p.front_zfail != n.front_zfail)
				{
					glStencilOpSeparate(GL_BACK, n.front_fail, n.front_zfail, n.front_pass);

					p.front_pass = n.front_pass;
					p.front_fail = n.front_fail;
					p.front_zfail = n.front_zfail;
				}

				// 뒷면
				if (p.back_func != n.back_func ||
					p.mask_read != n.mask_read)
				{
					glStencilFuncSeparate(GL_FRONT, n.back_func, 0, n.mask_read);

					p.back_func = n.back_func;
				}

				if (p.back_pass != n.back_pass ||
					p.back_fail != n.back_fail ||
					p.back_zfail != n.back_zfail)
				{
					glStencilOpSeparate(GL_FRONT, n.back_fail, n.back_zfail, n.back_pass);

					p.back_pass = n.back_pass;
					p.back_fail = n.back_fail;
					p.back_zfail = n.back_zfail;
				}

				//
				p.mask_read = n.mask_read;
			}
			else
			{
				if (p.front_func != n.front_func ||
					p.mask_read != n.mask_read)
				{
					glStencilFunc(n.front_func, 0, n.mask_read);

					p.front_func = n.front_func;
					p.mask_read = n.mask_read;
				}

				if (p.front_pass != n.front_pass ||
					p.front_fail != n.front_fail ||
					p.front_zfail != n.front_zfail)
				{
					glStencilOp(n.front_fail, n.front_zfail, n.front_pass);

					p.front_pass = n.front_pass;
					p.front_fail = n.front_fail;
					p.front_zfail = n.front_zfail;
				}
			}

			// 쓰기 마스크
			if (p.mask_write != n.mask_write)
			{
				// 마스크
				p.mask_write = n.mask_write;

				glStencilMask(n.mask_write);
			}

			p.stencil = n.stencil;
		}
	}
}

//
void GLRdh::_CommitRaszState() throw()
{
	const GLRaszProperty& n = _pend.rasz_prop;
	GLRaszProperty& p = _sion.rasz_prop;

	// 채움
	if (QglSupportPolygonMode() && p.fill != n.fill)
	{
		QglPolygonMode(GL_FRONT_AND_BACK, n.fill);

		p.fill = n.fill;
	}

	// 컬
	if (p.cull != n.cull)
	{
		if (n.cull == GL_NONE)
			glDisable(GL_CULL_FACE);
		else
		{
			if (p.cull == GL_NONE)
				glEnable(GL_CULL_FACE);

			glCullFace(n.cull);
		}

		p.cull = n.cull;
	}

	// 뎁스 비아스
	if (!k_eq(p.depth_bias, n.depth_bias) ||
		!k_eq(p.slope_scale, n.slope_scale))
	{
		float db = n.depth_bias * float((1 << 24) - 1);

		if (k_eq(db, 0.0f) && k_eq(n.slope_scale, 0.0f))
		{
			glDisable(GL_POLYGON_OFFSET_FILL);

			if (QglSupportPolygonMode())
			{
				glDisable(GL_POLYGON_OFFSET_LINE);
				glDisable(GL_POLYGON_OFFSET_POINT);
			}
		}
		else
		{
			if (k_eq(p.depth_bias, 0.0f) && k_eq(p.slope_scale, 0.0f))
			{
				glEnable(GL_POLYGON_OFFSET_FILL);

				if (QglSupportPolygonMode())
				{
					glEnable(GL_POLYGON_OFFSET_LINE);
					glEnable(GL_POLYGON_OFFSET_POINT);
				}
			}

			glPolygonOffset(n.slope_scale, db);
		}

		p.depth_bias = n.depth_bias;
		p.slope_scale = n.slope_scale;
	}
}

//
void GLRdh::_CommitBlndState() throw()
{
	GLint maxcnt = QglGetMaxOffScreenCount();
	bool alphasettle = false;

	// 훌
	for (GLint index = 0; index < maxcnt; index++)
	{
		if (index > 0 && _pend.off_ptrs[index] == NULL)
		{
			// 1번 이상이고 오프 스크린이 없으면 하지 말자
			continue;
		}

		const GLBlndProperty& n = _pend.blnd_props[index];
		GLBlndProperty& p = _sion.blnd_props[index];

		// 알파 블렌드
		if (n.enable)
		{
			if (!p.enable)
			{
				QglEnableIndexed(GL_BLEND, index);

				p.enable = true;
			}

			if (QglSupportSeparateAlphaBlend())
			{
				// 알파 분리가 가능함
				if (n.separate)
				{
					// 알파 포함
					if (p.color_op != n.color_op ||
						p.alpha_op != n.alpha_op)
					{
						QglBlendEquationSeparatei(index, n.color_op, n.alpha_op);

						p.color_op = n.color_op;
						p.alpha_op = n.alpha_op;
					}

					if (p.color_src != n.color_src || p.color_dst != n.color_dst ||
						p.alpha_src != n.alpha_src || p.alpha_dst != n.alpha_dst)
					{
						QglBlendFuncSeparatei(index, n.color_src, n.color_dst, n.alpha_src, n.alpha_dst);

						p.color_src = n.color_src;
						p.color_dst = n.color_dst;
						p.alpha_src = n.alpha_src;
						p.alpha_dst = n.alpha_dst;
					}

					// OSX에서 인텔 카드일 경우 flush 해야함
					// 여기서 상태가 다르면
					// glFlush();

					p.separate = true;
				}
				else
				{
					// 알파도 컬러로
					if (p.color_op != n.color_op)
					{
						QglBlendEquationi(index, n.color_op);

						p.color_op = n.color_op;
						//p.alpha_op = n.alpha_op;
					}

					if (p.color_src != n.color_src || p.color_dst != n.color_dst ||
						p.alpha_src != n.color_src || p.alpha_dst != n.color_dst)
					{
						QglBlendFunci(index, n.color_src, n.color_dst);

						p.color_src = n.color_src;
						p.color_dst = n.color_dst;
						p.alpha_src = n.color_src;
						p.alpha_dst = n.color_dst;
					}

					p.separate = false;
				}
			}
			else
			{
				// 알파 분리가 안됨
				if (alphasettle)
				{
					if (p.separate != n.separate ||
						p.color_op != n.color_op || p.color_src != n.color_src || p.color_dst != n.color_dst ||
						(n.separate && (p.alpha_src != n.alpha_src || p.alpha_dst != n.alpha_dst)))
					{
						// OSX 지원 문제
					}
				}
				else
				{
					// 모든 스테이지에 다 적용됨
					if (n.separate)
					{
						if (p.color_op != n.color_op || p.alpha_op != n.alpha_op)
							QglBlendEquationSeparate(n.color_op, n.alpha_op);

						if (p.color_src != n.color_src || p.color_dst != n.color_dst ||
							p.alpha_src != n.alpha_src || p.alpha_dst != n.alpha_dst)
							QglBlendFuncSeparate(n.color_src, n.color_dst, n.alpha_src, n.alpha_dst);

						for (GLint q = 0; q < maxcnt; q++)
						{
							GLBlndProperty& c = _sion.blnd_props[q];
							c.separate = true;
							c.color_op = n.color_op;
							c.color_src = n.color_src;
							c.color_dst = n.color_dst;
							c.alpha_op = n.alpha_op;
							c.alpha_src = n.alpha_src;
							c.alpha_dst = n.alpha_dst;
						}
					}
					else
					{
						if (p.color_op != n.color_op)
							glBlendEquation(n.color_op);

						if (p.color_src != n.color_src || p.color_dst != n.color_dst ||
							p.alpha_src != n.color_src || p.alpha_dst != n.color_dst)
							glBlendFunc(n.color_src, n.color_dst);

						for (GLint q = 0; q < maxcnt; q++)
						{
							GLBlndProperty& c = _sion.blnd_props[q];
							c.separate = false;
							c.color_op = n.color_op;
							c.color_src = n.color_src;
							c.color_dst = n.color_dst;
							//c.alpha_op = n.color_op;
							c.alpha_src = n.color_src;
							c.alpha_dst = n.color_dst;
						}
					}

					alphasettle = true;
				}
			}
		}
		else
		{
			if (p.enable)
			{
				QglDisableIndexed(GL_BLEND, index);

				p.enable = false;
			}
		}

		// 마스크
		if (p.mask_r != n.mask_r ||
			p.mask_g != n.mask_g ||
			p.mask_b != n.mask_b ||
			p.mask_a != n.mask_a)
		{
			QglColorMaski(index, n.mask_r, n.mask_g, n.mask_b, n.mask_a);

			p.mask_r = n.mask_r;
			p.mask_g = n.mask_g;
			p.mask_b = n.mask_b;
			p.mask_a = n.mask_a;
		}
	}
}

//
qgResObj* GLRdh::_RoamInitDhslState(const qbDhslInitializer& inz)
{
	GLDhslState* base = kobj_create(GLDhslState);
	qbDhslData& d = base->_data;
	GLDhslProperty& p = base->_prop;

	d = inz.DhslData;

	p.zenable = d.depth_func != QGCOP_ALWAYS || d.depth_write;
	p.zwrite = d.depth_write;
	p.zfunc = GLConvert::ComparisonOp(d.depth_func);

	p.stencil = d.front_enable || d.back_enable;
	p.twoside = d.back_enable;
	p.front_func = GLConvert::ComparisonOp(d.front_func);
	p.front_fail = GLConvert::StencilOp(d.front_fail);
	p.front_pass = GLConvert::StencilOp(d.front_pass);
	p.front_zfail = GLConvert::StencilOp(d.front_depth_fail);
	p.back_func = GLConvert::ComparisonOp(d.back_func);
	p.back_fail = GLConvert::StencilOp(d.back_fail);
	p.back_pass = GLConvert::StencilOp(d.back_pass);
	p.back_zfail = GLConvert::StencilOp(d.back_depth_fail);
	p.mask_read = d.mask_read;
	p.mask_write = d.mask_write;

	return base;
}

//
qgResObj* GLRdh::_RoamInitRaszState(const qbRaszInitializer& inz)
{
	GLRaszState* base = kobj_create(GLRaszState);
	qbRaszData& d = base->_data;
	GLRaszProperty& p = base->_prop;

	d = inz.RaszData;

	p.fill = d.fill == QGFILL_POINT ? GL_POINT : d.fill == QGFILL_WIRE ? GL_LINE : GL_FILL;
	p.cull = d.cull == QGCULL_FRONT ? GL_FRONT : d.cull == QGCULL_BACK ? GL_BACK : GL_NONE;

	p.depth_bias = d.depth_bias;
	p.slope_scale = d.slope_scale;

	return base;
}

//
qgResObj* GLRdh::_RoamInitBlndState(const qbBlndInitializer& inz)
{
	GLBlndState* base = kobj_create(GLBlndState);

	for (GLint i = 0; i < QglGetMaxOffScreenCount(); i++)
	{
		const qbBlndData& d = inz.UseIndependent ? inz.BlndData[i] : inz.BlndData[0];
		GLBlndProperty& p = base->_prop[i];

		base->_data[i] = d;

		p.enable =
			d.color_op != QGBLO_ADD || d.color_src != QGBLF_ONE || d.color_dest != QGBLF_ZERO ||
			d.alpha_op != QGBLO_ADD || d.alpha_src != QGBLF_ONE || d.alpha_dest != QGBLF_ZERO;
		p.separate =
			d.color_dest != d.alpha_dest ||
			d.color_src != d.alpha_src;

		p.color_op = GLConvert::BlendOp(d.color_op);
		p.color_src = GLConvert::BlendFactor(d.color_src);
		p.color_dst = GLConvert::BlendFactor(d.color_dest);

		p.alpha_op = GLConvert::BlendOp(d.alpha_op);
		p.alpha_src = GLConvert::BlendFactor(d.alpha_src);
		p.alpha_dst = GLConvert::BlendFactor(d.alpha_dest);

		p.mask_r = K_OFLAG(d.mask, QGMASK_RED);
		p.mask_g = K_OFLAG(d.mask, QGMASK_GREEN);
		p.mask_b = K_OFLAG(d.mask, QGMASK_BLUE);
		p.mask_a = K_OFLAG(d.mask, QGMASK_ALPHA);
	}

	return base;
}

//
qgResObj* GLRdh::_RoamInitSplrState(const qbSplrInitializer& inz)
{
	GLSplrState* base = kobj_create(GLSplrState);
	qbSplrData& d = base->_data;
	GLSplrState::Info& o = base->_info;

	d = inz.SplrData;

	bool copmip = d.cop != QGCOP_NEVER;

	GLConvert::Filter(d.filt, copmip, &o.mag_filter, &o.min_filter);

	o.wrap_s = GLConvert::Address(d.addru);
	o.wrap_t = GLConvert::Address(d.addrv);
	o.wrap_r = GLConvert::Address(d.addrw);

	if (!copmip)
		o.cop_mode = GL_NONE;
	else
	{
		o.cop_mode = GL_COMPARE_REF_TO_TEXTURE;
		o.cop_func = GL_LESS;
	}

	o.lod_bias = d.lod_bias;

	return base;
}

#endif	// _QG_USE_GL
